<?php

class Nuo_Document
{

	/**
	 * Store theme name
	 * @var string
	 */
	protected $_theme = null;

	/**
	 * Store style name
	 * @var string
	 */
	protected $_style = null;

	/**
	 * Store document name
	 * @var string
	 */
	protected $_name = null;
	/**
	 * Store title
	 * @var array
	 */
	protected $_title = array();

	/**
	 * Store keywords
	 * @var array
	 */
	protected $_keyword = array();

	/**
	 * Store meta data
	 * @var array
	 */
	protected $_meta = array();

	/**
	 * Store description to display in "meta" tag
	 * @var array
	 */
	protected $_description = array();

	/**
	 * Store head image
	 * @var array
	 */
	protected $_image = null;

	/**
	 * Store head link
	 * @var array
	 */
	protected $_link = array();

	/**
	 * Js Config data
	 * @access protected
	 * @var    array
	 */
	protected $_jsConfig = array();

	/**
	 * Store javascript head script
	 * @var array
	 */
	protected $_headJs = array();

	/**
	 * Store stylesheet script
	 */
	protected $_headCss = array();

	/**
	 * store js file
	 * @var array
	 */
	protected $_headJsFile = array();

	/**
	 * store css file
	 */
	protected $_headCssFile = array();

	/**
	 * Store javascript head script
	 * @var array
	 */
	protected $_js = array();

	/**
	 * Store stylesheet script
	 */
	protected $_css = array();

	/**
	 * store js file
	 * @var array
	 */
	protected $_jsFile = array();

	/**
	 * store css file
	 */
	protected $_cssFile = array();

	/**
	 * @access  public
	 * @return  Nuo_Response
	 */
	function setTheme($theme)
	{
		$this -> _theme = (string)$theme;
		return $this;
	}

	/**
	 * Get theme name
	 * @access  public
	 * @return  string
	 */
	function getTheme()
	{
		if (NULL == $this -> _theme)
		{
			$this -> _theme = Nuo::getConfig('core/site', 'default_theme');
		}

		return $this -> _theme;
	}

	/**
	 * Set action name
	 * @access  public
	 * @param   string $name
	 * @return  Nuo_Response
	 */
	public function setName($name)
	{
		$this -> _name = (string)$name;
		return $this;
	}

	/**
	 * Get action name
	 * @access  public
	 * @return  string
	 */
	public function getName()
	{
		if (NULL == $this -> _name)
		{
			$this -> _name = Nuo::getRequest() -> getName();
		}
		return $this -> _name;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function setTitle($title)
	{
		$this -> _title[] = $title;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addKeyword($keyword)
	{
		$this -> _keyword[] = $keyword;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addDescription($description)
	{
		$this -> _desription[] = $description;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addMeta($meta)
	{
		$this -> _meta[] = $meta;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  Nuo_Document
	 */
	public function addJs($string)
	{
		$this -> _js[] = $string;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addCss($string)
	{
		$this -> _css[] = $string;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addHeadJs($string)
	{
		$this -> _headJs[] = $string;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addHeadCss($string)
	{
		$this -> _headCss[] = $string;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function _buildJsFile($value)
	{
		if (is_string($value))
		{
			$value = array('src' => $value);
		}
		return array_merge(array('type' => 'text/javascript'), $value);
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addJsFile($value)
	{
		$this -> _jsFile[] = $this -> _buildJsFile($value);
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addHeadJsFile($value)
	{
		$this -> _headJsFile[] = $this -> _buildJsFile($value);
		;
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	protected function _buildCssFile($value)
	{
		if (is_string($value))
		{
			$value = array('href' => $value);
		}
		return array_merge(array(
			'type' => 'text/css',
			'rel' => 'stylesheet',
			'media' => 'all'
		), $value);
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addHeadCssFile($value)
	{
		$this -> _headCssFile[] = $this -> _buildCssFile($value);
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addCssFile($value)
	{
		$this -> _cssFile[] = $this -> _buildCssFile($value);
		return $this;
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function _buildLink($value)
	{
		if (is_string($value))
		{
			$value = array('href' => $value);
		}
		return array_merge(array(
			'type' => 'text/css',
			'rel' => 'stylesheet',
			'media' => 'all'
		), $value);
	}

	/**
	 *
	 * @access  public
	 * @param   array/string $value
	 * @return  array
	 */
	public function addLink($link)
	{
		$this -> _link[] = $link;
		return $this;
	}

	/**
	 * Get action type
	 * @access  public
	 * @access  public
	 * @return  string  [admincp,browse,mobile,...]
	 */
	function getActionType()
	{
		$name = $this -> getName();

		$parts = explode('/', $name);

		if (isset($parts[2]))
		{
			return $parts[2];
		}

		return 'browse';
	}

	/**
	 * Get xhtml title tag content
	 * @access  public
	 * @return  string
	 */
	public function renderTitle()
	{

		if (NULL == $this -> _title)
		{
			$this -> _title = array(Nuo::getConfig('core/site', 'title'));
		}
		$glue = Nuo::getConfig('core/site', 'glue');

		return sprintf('<title>%s</title>', implode($glue, $this -> _title));
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderBase()
	{
		return '<base href="' . Nuo::getUrl('/') . '"/>';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderOpengraph()
	{
		return '';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderJs()
	{
		if ($this -> _js)
		{
			return '<script type="text/javascript">' . implode(';' . PHP_EOL, $this -> _js) . '</script>';
		}

		return '';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderHeadJs()
	{
		if ($this -> _headJs)
		{
			return '<script type="text/javascript">' . implode(';' . PHP_EOL, $this -> _headJs) . '</script>';
		}

		return '';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderJsFile()
	{
		$xhtml = array();

		foreach ($this->_cssFile as $value)
		{
			$xhtml[] = '<script ' . $this -> flat($value) . '></script>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderHeadJsFile()
	{
		$xhtml = array();

		foreach ($this->_headJsFile as $value)
		{
			$xhtml[] = '<script ' . $this -> flat($value) . '></script>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * Get scripts  <script src="./files">
	 * @access  public
	 * @return  string  xhtml string
	 */
	public function renderJsConfig()
	{
		$this -> jsConfig['basePath'] = Nuo::getUrl('/');
		$this -> jsConfig['staticUrl'] = Nuo::getStaticUrl('/');
		$js = json_encode($this -> jsConfig);
		return '<script type="text/javascript">var _config =' . $js . ';</script>';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderLink()
	{
		$xhtml = array();

		foreach ($this->_link as $value)
		{
			$xhtml[] = '<link ' . $this -> flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderCss()
	{
		if ($this -> _css)
		{
			return '<style type="text/css">' . implode(';' . PHP_EOL, $style) . '</style>';
		}

		return '';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderCssFile()
	{
		$xhtml = array();

		foreach ($this->_cssFile as $value)
		{
			$xhtml[] = '<link ' . $this -> flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderHeadCss()
	{
		if ($this -> _headCss)
		{
			return '<style type="text/css">' . implode(';' . PHP_EOL, $style) . '</style>';
		}

		return '';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderHeadCssFile()
	{
		$xhtml = array();

		foreach ($this->_headCssFile as $value)
		{
			$xhtml[] = '<link ' . $this -> flat($value) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderMeta()
	{
		$xhtml = array();

		foreach ($this->_meta as $meta)
		{
			$xhtml[] = '<meta ' . $this -> flat($meta) . '/>';
		}
		return implode(PHP_EOL, $xhtml);
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderKeyword()
	{
		if (empty($this -> _keyword))
		{
			$this -> _keyword = array(Nuo::getConfig('core/site', 'keywords'));
		}
		return '<meta name="keyword" content="' . implode(', ', $this -> _keyword) . '"/>';
	}

	/**
	 * flatten meta to html
	 * @access  public
	 * @return  string $xhtml string
	 */
	public function renderDescription()
	{

		if (empty($this -> _description))
		{
			$this -> _description = array(Nuo::getConfig('core/site', 'description'));
		}
		return '<meta name="description" content="' . implode(', ', $this -> _description) . '"/>';
	}

	/**
	 * Compile region to xhtml
	 * @access public
	 * @param   int    $regionId
	 * @param   array  $region
	 * @return  string
	 */
	public function renderBlock($regionId, $region)
	{

		// test regions.
		$xhtml = array();

		foreach ($region as $element)
		{
			$name = $element['widget_key'];
			$content = '';

			if ('content' == $name)
			{
				$content = Nuo::getResponse() -> getContent();

			}
			else
			if ($name)
			{
				$params = NULL;

				/**
				 * create request
				 */
				$content = Nuo::renderWidget($name, $params);
			}

			if ($content)
			{
				/**
				 * wrap content here
				 */
				$xhtml[] = '<div class="uiBlock">' . $content . '</div>';
				unset($content);
			}
			unset($request);
		}

		/**
		 * call a process to render all items here.
		 * protect layout by this call.
		 */

		$xhtml = implode(PHP_EOL, $xhtml);

		if ($xhtml)
		{
			return '<div class="uiRegion ' . $regionId . '">' . $xhtml . '</div>';
		}
	}

	/**
	 * @return string
	 */
	public function renderHead($theme, $actionType)
	{

		$staticUrl = Nuo::getConfig('core/site', 'staticUrl');

		if (!$staticUrl)
		{
			$staticUrl = Nuo::getUrl('static');
		}

		/**
		 * @todo check session work
		 * check messenger
		 */
		if ($msg = Nuo_Messenger::instance() -> get())
		{
			$this -> addJs('$(document).ready(function(){nuo.flashMsg("' . $msg . '")});');
		}

		/**
		 * process some thing else as content or not of page.
		 */
		$this -> addMeta(array(
			'http-equiv' => 'content-type',
			'content' => 'text/xhtml+html; charset=utf-8'
		)) -> addMeta(array(
			'http-equiv' => 'content-language',
			'content' => 'en-us',
		)) -> addLink(array(
			'rel' => 'shortcut icon',
			'type' => 'image/x-icon',
			'href' => $staticUrl . '/favicon.ico'
		));

		// add main js

		$cacheKey = 'site/media/' . $actionType . '/' . $theme;

		$media = Nuo_Cache::instance() -> read($cacheKey);

		if ($media == FALSE)
		{
			$media = $this -> getMediaFiles($theme, $actionType);

			Nuo_Cache::instance() -> write($cacheKey, $media);
		}

		foreach ($media['js'] as $group => $srcs)
		{
			foreach ($srcs as $src)
			{
				$this -> addHeadJsFile($staticUrl . $src);
			}
		}

		foreach ($media['css'] as $group => $srcs)
		{
			foreach ($srcs as $src)
			{
				$this -> addHeadCssFile($staticUrl . $src);
			}
		}

		$this -> addHeadCssFile($staticUrl . '/theme/' . $theme . '/styles/theme.css');

		/**
		 * @var array
		 */
		$allows = array(
			'Base',
			'Title',
			'Keyword',
			'Description',
			'Meta',
			'Link',
			'HeadCssFile',
			'Css',
			'JsConfig',
			'HeadJsFile',
			'Css',
			'Js'
		);
		/**
		 * @var array
		 */
		$xhtml = array();

		foreach ($allows as $allow)
		{
			if (($html = $this -> {'render' . $allow}()) != '')
			{
				$xhtml[] = $html;
			}
		}

		return implode(PHP_EOL, $xhtml);
	}

	function getMediaFiles($theme, $actionType)
	{
		$media = array();

		$modules = Nuo::getModules();

		foreach ($modules as $module=>$spec)
		{
			$file = MODULE_DIR . $module . '/setting/media/' . $actionType . '.php';

			if (file_exists($file))
			{
				$data =
				include $file;

				// all data js
				if ($data['js'])
				{
					foreach ($data['js'] as $group => $srcs)
					{
						foreach ($srcs as $src)
						{
							$media['js'][$group][] = $src;
						}
					}
				}

				// all data css
				if ($data['css'])
				{
					foreach ($data['css'] as $group => $srcs)
					{
						foreach ($srcs as $src)
						{
							$media['css'][$group][] = $src;
						}
					}
				}
			}
		}
		ksort($media['js']);
		ksort($media['css']);
		return $media;
	}

	/**
	 * Compile region to xhtml
	 * @return string xhtml string
	 */
	public function render()
	{

		$name = $this -> getName();

		$actionType = $this -> getActionType();

		$theme = $this -> getTheme();

		$config = $this -> loadSectionIds($name, $theme);

		$cache = Nuo_Cache::instance();

		$xhtml = array();
		$regionIds = array();
		$majorRegionIds = array();
		$layouts = array();

		foreach ($config as $key => $id)
		{
			$structure = NULL;

			if ($id)
			{
				// fetch load cache data
				$cacheKey = $theme . '/layout/' . $id;
				$structure = $cache -> read($cacheKey);

				if (FALSE == $structure)
				{
					$structure = $this -> loadBlock($id);
					Nuo_Cache::instance() -> write($cacheKey, $structure);
				}
			}
			
			if ($structure)
			{
				$section = $structure['section'];

				foreach ($structure['regions'] as $regionId => $region)
				{
					$xhtml[$regionId] = $this -> renderBlock($regionId, $region);
					$regionIds[$regionId] = 1;
					$major = $regionId;
					if (($pos = strpos($major, '_')) > 0)
					{
						$major = substr($major, 0, $pos);
					}
					$majorRegionIds[$major] = $major;
				}
				$layout = $key . ucfirst($section['layout_id']);
				$layouts[$key] = $layout;
			}
		}

		if (isset($majorRegionIds['column']) && isset($majorRegionIds['column2']))
		{
			$layouts['bd'] = 'col30';
		}
		else
		if (isset($majorRegionIds['column']))
		{
			$layouts['bd'] = 'col20';
		}
		else
		if (isset($majorRegionIds['column2']))
		{
			$layouts['bd'] = 'col21';
		}

		$script = 'core/layout/master/template';
		$bodyClass = str_replace('_controller_', '_', str_replace('/', '_', $this -> getName()));

		$bodyXhtml = Nuo::getView() -> render($script, array(
			'xhtml' => $xhtml,
			'regionIds' => $regionIds,
			'majorRegionIds' => $majorRegionIds,
			'layouts' => $layouts,
			'bodyClass' => $bodyClass,
		));
		unset($xhtml);

		return '<!DOCTYPE html><html lang="en" ><head>' . $this -> renderHead($theme, $actionType) . '</head>' . $bodyXhtml . '</html>';
	}

	/**
	 * load theme structure for theme
	 * @access  public
	 * @param   string $theme
	 * @return  array
	 */
	public function loadLayout($theme)
	{
		if (NULL == $theme)
		{
			$theme = 'default';
		}

		$rows = Nuo::getTable('core/layout') -> loadForRender($theme);


		if (empty($rows))
		{
			trigger_error(strtr('load theme data "{theme}"', array('{theme}' => $theme)), E_USER_WARNING);
		}

		return $rows;
	}

	/**
	 * Get section structure
	 * @access  public
	 * @param   int   $layoutId
	 * @return  string
	 */
	public function loadBlock($layoutId)
	{
		$return = Nuo::getTable('core/block') -> loadForRender($layoutId);

		return $return;
	}

	/**
	 * Get section ids list by name
	 * @access  public
	 * @param   string  $name
	 * @param   string  $theme
	 * @return  array   {header=>id, content=>id,footer=>id}
	 */
	public function loadSectionIds($name, $theme)
	{
		$cache = Nuo_Cache::instance();

		$cacheKey = 'core/theme/' . $theme;

		$data = NULL;

		if (FALSE == ($data = $cache -> read($cacheKey)))
		{
			$data = $this -> loadLayout($theme);
			
			$cache -> write($cacheKey, $data);
		}
		

		/**
		 * @TODO: define log level to check log.
		 */
		$sections = array(
			'hd' => NULL,
			'bd' => NULL,
			'ft' => NULL
		);

		$type = $this -> getActionType();

		foreach ($sections as $key => $id)
		{
			// build a quick check from path.
			$check = sprintf('%s:%s', $key, $name);
			$default = sprintf('%s:%s', $key, $type);

			do
			{
				if (isset($data[$check]) && $data[$check])
				{
					$id = $data[$check];
					break;
				}

				if (0 < ($pos = strrpos($check, '/')))
				{
					$check = substr($check, 0, $pos);
				}
			}
			while($pos);

			if (NULL == $id && isset($data[$default]))
			{
				$id = $data[$default];
			}

			$sections[$key] = $id;
		}

		return $sections;
	}

	/**
	 * @static
	 * @access  public
	 * @param   array   $array
	 * @return  string  xhtml string
	 */
	static public function flat($array)
	{
		$xhtml = array();
		if (is_array($array))
		{
			foreach ($array as $key => $value)
			{
				$xhtml[] = sprintf('%s="%s"', $key, $value);
			}
		}

		return implode(' ', $xhtml);
	}

}
